package com.xucia.strands;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;

import titt.io.IOFactory;
import titt.util.io.pipe.StreamPiper;
public class Strands {
	static ScriptableObject compiler;

	private static String getResourceAsString(InputStream is) throws IOException {
		BufferedReader in = new BufferedReader(new InputStreamReader(is));
		StringBuffer buffer = new StringBuffer();
		String line;
		while ((line = in.readLine()) != null) {
			buffer.append(line);
			buffer.append("\n");
		}
		return buffer.toString();
	}
	static Pattern PJS_SUBSECTION_PATTERN = Pattern.compile("/\\*<persevere>(.*?)\\*/(.*?)/\\*</persevere>(.*?)\\*/",Pattern.DOTALL);
	static boolean debug = true;

/*	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String target = request.getParameter("target");
		response.getOutputStream().print(compileFile(getServletContext().getRealPath(target)));
	}*/
	private static String compileFile(String filename,boolean getters,boolean setters,boolean debug) throws IOException {
		return compileString(getResourceAsString(new FileInputStream(filename)),getters,setters,false,debug,filename.endsWith(".js17"),"source");
	}
	
	private static ArrayList<File> getFiles(File f) {
		ArrayList<File> retFiles = new ArrayList<File>();
		
		File[] files = f.listFiles();

		for(File fi : files) {
			if(fi.isDirectory())
				retFiles.addAll(getFiles(fi));
			else
				retFiles.add(fi);
		}
		
		return retFiles;
	}
	
	public static File compileTITT(File out) throws Exception {
		String base = "../TITT/javascript/";
		
		ArrayList<File> files = getFiles(new File(base));
		
		Collections.sort(files, new Comparator<File>() {

			public int compare(File arg0, File arg1) {
				try {
					
					BufferedReader a = new BufferedReader(new FileReader(arg0));
					BufferedReader b = new BufferedReader(new FileReader(arg1));
					
					String as = a.readLine();
					String bs = b.readLine();

					a.close();
					b.close();
					
					if(as == null) return  1;
					if(bs == null) return -1;
					
//					if(as.startsWith("//")) {
//						System.out.println("foo");
//					}
//					
//					if(as.startsWith("//need " + arg1.getName())) {
//						System.out.println("hmm");
//						return  1;
//					}
//					if(bs.startsWith("//need " + arg0.getName())) {
//						System.out.println("what");
//						return -1;
//					}
//					
					if(as.startsWith("//dep")) return  1;
					if(bs.startsWith("//dep")) return -1;

					if(as.startsWith("window.")) return  1;
					if(bs.startsWith("window.")) return -1;
					

				}
				catch(Exception e) {
					
				}
				return 0;
			}
		});
		
		BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(out));

		StreamPiper piper = IOFactory.createStreamPiper();
		
		File strands = new File("../StrandsCompiler/strands.js");
		
		files.add(0, strands);
		
		for(File f : files) {
			if(!f.getName().endsWith(".js"))
				continue;
			BufferedInputStream in = new BufferedInputStream(new FileInputStream(f));
			piper.pipe(in, bout, (int)f.length());
			bout.write('\n');
			in.close();
		}
		
		bout.close();
		
		return out;
	}
	
	
	public static String compileFile(String filename, boolean debug) throws IOException {
		return compileFile(filename,false,false,debug);
	}
	public static String compileFile(String filename) throws IOException {
		return compileFile(filename,false,false,false);
	}
	public static String compileString(String source, boolean debug) throws IOException {
		return compileString(source,false,false,false,debug,false,"source");
	}
	public static String compileString(String source) throws IOException {
		return compileString(source,false);
	}
	private static String compileString(String fileContents,boolean getters,boolean setters, boolean expression,boolean debug, boolean js17, String filename) throws IOException {
				
		synchronized(Strands.class) {
			if (compiler == null) {
				InputStream is = Strands.class.getClassLoader().getResourceAsStream("com/xucia/strands/compiler.js");
				Context context = Context.enter();
				if (is == null)
					throw new RuntimeException("compiler.js could not be found in the classpath");
				Scriptable global = Context.enter().initStandardObjects();
				String compilerSource = "_frm=false;strands={};\n" + getResourceAsString(is);
				context.evaluateString(global, compilerSource, "compiler", 1, null);
				ScriptableObject strands = (ScriptableObject) global.get("strands", global);
				compiler = (ScriptableObject) strands.get("compiler", strands);
			}
		}
		
		if (fileContents.startsWith("/*persevere-partial")) {
			Matcher m = PJS_SUBSECTION_PATTERN.matcher(fileContents);
			StringBuffer sb = new StringBuffer(); 
			while (m.find())
			{ 
				String openingMassager =m.group(1);
				String njsText = m.group(2);
				String closingMassager= m.group(3); 
				njsText = njsText.replaceAll("//NJS","");
				String targetSource = openingMassager + "\n" +  njsText + "\n" + closingMassager;
				String compiledCode = (String) ScriptableObject.callMethod(compiler, "tryCatchCompile", new Object[]{targetSource,filename,new Boolean(getters),new Boolean(setters),new Boolean(debug),new Boolean(expression),new Boolean(js17)});
				//String compiledCode = (String) Main.shellContextFactory.call(iproxy);
				compiledCode =compiledCode.substring(0,closingMassager.length()==0?compiledCode.length():compiledCode.lastIndexOf(closingMassager));
				compiledCode = compiledCode.replaceAll("\\$","\\\\\\$"); // escape the $ so they don't trigger group capture
				m.appendReplacement(sb,compiledCode);
			}
			m.appendTail(sb);
			return sb.toString();
		}
		else {
			return (String) ScriptableObject.callMethod(compiler, "tryCatchCompile", new Object[]{fileContents,filename,new Boolean(getters),new Boolean(setters),new Boolean(debug),new Boolean(expression),new Boolean(js17)});
		}
	}



}
